<?php
/* 
    Name：FastPHP
    CopyRight: Mr.Fu 2019SR0832915
*/
namespace app\extend\unionpay;
/*
	银联支付接口插件 
*/
use \app\base\common;
use \app\extend\unionpay\CertUtil;

class Unionpay extends common
{
    public function _initialize()
    {
    	//执行父类的构造方法
    	parent::_initialize();
		
		$this->Unionpay_Config['merId']='310420154990011';
		$this->Unionpay_Config['ifValidateRemoteCert']='false';
		$this->Unionpay_Config['ifValidateCNName']='false';
		//证书
		$this->Unionpay_Config['signCertPwd']='123456';
		$this->Unionpay_Config['signCertPath']=__DIR__.'/certs/manyibuy.pfx';
		$this->Unionpay_Config['encryptCertPath']=__DIR__.'/certs/acp_prod_enc.cer';
		$this->Unionpay_Config['rootCertPath']=__DIR__.'/certs/acp_prod_root.cer';
		$this->Unionpay_Config['middleCertPath']=__DIR__.'/certs/acp_prod_middle.cer';
		
		//接口地址
		$this->Unionpay_Config['frontTransUrl']='https://gateway.95516.com/gateway/api/frontTransReq.do';
		$this->Unionpay_Config['backTransUrl']='https://gateway.95516.com/gateway/api/backTransReq.do';
		$this->Unionpay_Config['singleQueryUrl']='https://gateway.95516.com/gateway/api/queryTrans.do';
		$this->Unionpay_Config['batchTransUrl']='https://gateway.95516.com/gateway/api/batchTrans.do';
		$this->Unionpay_Config['fileTransUrl']='https://filedownload.95516.com/';
		$this->Unionpay_Config['appTransUrl']='https://gateway.95516.com/gateway/api/appTransReq.do';
		$this->Unionpay_Config['cardTransUrl']='https://gateway.95516.com/gateway/api/cardTransReq.do';
		
		//回调
		$this->Unionpay_Config['frontUrl']=$this->Config['weburl'].'/index.php/index/Unionpay/Unionpay_callback';
		$this->Unionpay_Config['backUrl']=$this->Config['weburl'].'/index.php/index/Unionpay/Unionpay_callback';
		
		$this->Unionpay_Config['app_frontUrl']=$this->Config['weburl'].'/index.php/mobile/Unionpay/Unionpay_callback_show';
		$this->Unionpay_Config['app_backUrl']=$this->Config['weburl'].'/index.php/mobile/Unionpay/Unionpay_callback';
    }
	/*
	 *		  基本配置 
	 */	
	public $Unionpay_Config=array(
		//支付、查询等相关接口(生产环境)
		'frontTransUrl'=>'',
		'backTransUrl'=>'',
		'singleQueryUrl'=>'',
		'batchTransUrl'=>'',
		'fileTransUrl'=>'',
		'appTransUrl'=>'',
		'cardTransUrl'=>'',
		
		//签名方式
		'signMethod'=>'01',
		//商户ID
		'merId'=>'',
		//支付接口版本
		'version'=>'5.1.0',
		'ifValidateRemoteCert'=>'true',
		'ifValidateCNName'=>'true',
		//环境签名证书配置
		'signCertPath'=>'',
		//环境证书密码位数需小于等于6位
		'signCertPwd'=>'',
		//加密证书配置
		'encryptCertPath'=>'',
		//验签根证书
		'rootCertPath'=>'',
		//验签中级证书
		'middleCertPath'=>'',
		//前台通知地址，填写后台接收银联前台通知的地址
		'frontUrl'=>'',
		//后台通知地址，填写后台接收银联后台通知的地址，必须外网能访问
		'backUrl'=>'',
		
	);
	//发起支付
	public function pay($orderid,$amount=100,$channel='web')
	{
		$params = array(
			'version' => $this->Unionpay_Config['version'],              //版本号
			'encoding' => 'utf-8',				  						 //编码方式
			'txnType' => '01',				      						 //交易类型
			'txnSubType' => '01',				  						 //交易子类
			'bizType' => '000201',				  						 //业务类型
			'frontUrl' =>  $this->Unionpay_Config['frontUrl'],  		 //前台通知地址
			'backUrl' => $this->Unionpay_Config['backUrl'],	  			 //后台通知地址
			'signMethod' => $this->Unionpay_Config['signMethod'],	     //签名方法
			'channelType' => '08',	              						 //渠道类型，07-PC，08-手机
			'accessType' => '0',		          						 //接入类型
			'currencyCode' => '156',	          				//交易币种，境内商户固定156
			
			'merId' => $this->Unionpay_Config['merId'],		//商户代码，请改自己的测试商户号，此处默认取demo演示页面传递的参数
			'orderId' => $orderid,	//商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则
			'txnTime' => MyDate('YmdHis',time()),	//订单发送时间，格式为YYYYMMDDhhmmss，取北京时间，此处默认取demo演示页面传递的参数
			'txnAmt' => $amount*100,	//交易金额，单位分，此处默认取demo演示页面传递的参数
			'payTimeout' => date('YmdHis', strtotime('+15 minutes')), 
		);
		
		if($channel=='web'){
			$params['frontUrl']=$this->Unionpay_Config['frontUrl'];
			$params['backUrl']=$this->Unionpay_Config['backUrl'];
		}else if($channel=='app'){
			$params['frontUrl']=$this->Unionpay_Config['app_frontUrl'];
			$params['backUrl']=$this->Unionpay_Config['app_backUrl'];
		}
		
		$this->sign ( $params );
		$uri = $this->Unionpay_Config['frontTransUrl'];
		$html_form = Unionpay::createAutoFormHtml( $params, $uri );
		WLog('unionpay_log',$html_form );
		echo $html_form;
	}
	
	public static function createAutoFormHtml($params, $reqUrl) {
		$encodeType = isset ( $params ['encoding'] ) ? $params ['encoding'] : 'UTF-8';
		$html = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset={$encodeType}\" /></head><body onload=\"javascript:document.pay_form.submit();\"><form id=\"pay_form\" name=\"pay_form\" action=\"{$reqUrl}\" method=\"post\">";
		
		foreach ( $params as $key => $value ) {
			$html .= "<input type=\"hidden\" name=\"{$key}\" id=\"{$key}\" value=\"{$value}\" />\n";
		}
		
		$html .= "<input type=\"submit\"></form></body></html>";
		return $html;
	}
	
	//开始签名
	public function sign(&$params){
		//判断签名方式
		if($this->Unionpay_Config['signMethod']=='01'){
			return $this->signByCertInfo($params, $this->Unionpay_Config['signCertPath'],$this->Unionpay_Config['signCertPwd']);
		}else{
			return false;
		}
	}
	
	public function signByCertInfo(&$params, $cert_path, $cert_pwd)
	{
		if(isset($params['signature'])){
			unset($params['signature']);
		}
		//证书ID
		$params ['certId'] = CertUtil::getSignCertIdFromPfx($cert_path, $cert_pwd);
		$private_key = CertUtil::getSignKeyFromPfx( $cert_path, $cert_pwd );
		// 转换成key=val&串
		$params_str = $this->createLinkString ( $params, true, false );
		WLog('unionpay_log',"签名key=val&...串 >" . $params_str );
		
		if($params['version']=='5.1.0'){
			//sha256签名摘要
			$params_sha256x16 = hash( 'sha256',$params_str);
			
			WLog('unionpay_log', "摘要sha256x16 >" . $params_sha256x16 );
			
			// 签名
			$result = openssl_sign ( $params_sha256x16, $signature, $private_key, 'sha256');
			if ($result) {
				$signature_base64 = base64_encode ( $signature );
				WLog('unionpay_log', "签名串为 >" . $signature_base64 );
				$params ['signature'] = $signature_base64;
			} else {
				WLog('unionpay_log', ">>>>>签名失败<<<<<<<");
			}
		} else {
			WLog('unionpay_log', "wrong version: " + $params['version']);
			$result = false;
		}		
	}
	/**
	 * 讲数组转换为string
	 *
	 * @param $para 数组        	
	 * @param $sort 是否需要排序        	
	 * @param $encode 是否需要URL编码        	
	 * @return string
	 */	
	public function createLinkString($para, $sort, $encode) {
			
		if($para == NULL || !is_array($para))
			return "";
		
		$linkString = "";
		if ($sort) {
			$para = $this->argSort ( $para );
		}
		while ( list ( $key, $value ) = each ( $para ) ) {
			if ($encode) {
				$value = urlencode ( $value );
			}
			$linkString .= $key . "=" . $value . "&";
		}
		// 去掉最后一个&字符
		$linkString = substr ( $linkString, 0, count ( $linkString ) - 2 );
		
		return $linkString;
	} 
	/**
	 * 对数组排序
	 *
	 * @param $para 排序前的数组
	 *        	return 排序后的数组
	 */
	public function argSort($para) {
		ksort ( $para );
		reset ( $para );
		return $para;
	}
	
	//调用回调签名
	public function callback_validate($data)
	{
		//写入回调日志
		WLog('unionpay_callback_log', "回调原始数据：>>>>>".$this->createLinkString ( $data, false, true )."<<<<<<<");
		if(is_array($data) and (count($data)>1) and $this->uvalidate ($data) and ($data['respCode']=='00')){
			return true;
		}else{
			return false;
		}
	}
	//回调签名验证
	public function uvalidate($params)
	{
		$isSuccess = false;
		$signature_str = $params ['signature'];
		unset ( $params ['signature'] );
		$params_str = $this->createLinkString ( $params, true, false );

		$strCert = $params['signPubKeyCert'];
		$strCert = CertUtil::verifyAndGetVerifyCert($strCert,$this->Unionpay_Config);
		if($strCert == null){
			WLog('unionpay_callback_log', "回调验证失败：>>>>>".$params["signPubKeyCert"]."<<<<<<<");
			$isSuccess = false;
		} else {
			$params_sha256x16 = hash('sha256', $params_str);
			$signature = base64_decode ( $signature_str );
			$isSuccess = openssl_verify ( $params_sha256x16, $signature,$strCert, "sha256" );
			WLog('unionpay_callback_log', "回调验证成功：>>>>>".($isSuccess ? '验签成功' : '验签失败')."<<<<<<<");
		}
		return $isSuccess;		
	}
}
?>